package top.lshaci.learning.springboot.jpaquerydsl.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.dsl.*;

import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * DslConditionMatcher
 *
 * @author liangxu
 */
public enum DslConditionMatcher {
    /**
     * 字符串：等于
     */
    String_EQ {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return string(dslQueryField, StringPath::eq);
        }
    },
    /**
     * 字符串：小于
     */
    String_LT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return string(dslQueryField, StringPath::lt);
        }
    },
    /**
     * 字符串：小于等于
     */
    String_LE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return string(dslQueryField, StringPath::loe);
        }
    },
    /**
     * 字符串：大于
     */
    String_GT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return string(dslQueryField, StringPath::gt);
        }
    },
    /**
     * 字符串：大于等于
     */
    String_GE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return string(dslQueryField, StringPath::goe);
        }
    },
    /**
     * 字符串：包含
     */
    String_LK {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return string(dslQueryField, StringPath::contains);
        }
    },
    /**
     * 字符串：开始以
     */
    String_SW {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return string(dslQueryField, StringPath::startsWith);
        }
    },
    /**
     * 字符串：结束以
     */
    String_EW {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return string(dslQueryField, StringPath::endsWith);
        }
    },
    /**
     * 字符串：在……内
     */
    String_IN {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return stringIn(dslQueryField, StringPath::in);
        }
    },
    /**
     * 字符串：开始以任一
     */
    String_BSW {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return stringBatch(dslQueryField, StringPath::startsWith);
        }
    },
    /**
     * 字符串：结束以任一
     */
    String_BEW {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return stringBatch(dslQueryField, StringPath::endsWith);
        }
    },
    /**
     * 字符串：包含任一
     */
    String_BLK {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return stringBatch(dslQueryField, StringPath::contains);
        }
    },
    /**
     * 字符串：不在……内
     */
    String_NOT_IN {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return stringIn(dslQueryField, StringPath::notIn);
        }
    },
    /**
     * 数字：等于
     */
    Number_EQ {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return number(dslQueryField, NumberPath::eq);
        }
    },
    /**
     * 数字：小于
     */
    Number_LT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return number(dslQueryField, NumberPath::lt);
        }
    },
    /**
     * 数字：小于等于
     */
    Number_LE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return number(dslQueryField, NumberPath::loe);
        }
    },
    /**
     * 数字：大于
     */
    Number_GT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return number(dslQueryField, NumberPath::gt);
        }
    },
    /**
     * 数字：大于等于
     */
    Number_GE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return number(dslQueryField, NumberPath::goe);
        }
    },
    /**
     * 数字：在……内
     */
    Number_IN {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return numberIn(dslQueryField, NumberPath::in);
        }
    },
    /**
     * 数字：不在……内
     */
    Number_NOT_IN {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return numberIn(dslQueryField, NumberPath::notIn);
        }
    },
    /**
     * 日期：等于
     */
    Date_EQ {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return date(dslQueryField, DatePath::eq);
        }
    },
    /**
     * 日期：小于
     */
    Date_LT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return date(dslQueryField, DatePath::lt);
        }
    },
    /**
     * 日期：小于等于
     */
    Date_LE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return date(dslQueryField, DatePath::loe);
        }
    },
    /**
     * 日期：大于
     */
    Date_GT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return date(dslQueryField, DatePath::gt);
        }
    },
    /**
     * 日期：大于等于
     */
    Date_GE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return date(dslQueryField, DatePath::goe);
        }
    },
    /**
     * 时间：等于
     */
    Time_EQ {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return time(dslQueryField, TimePath::eq);
        }
    },
    /**
     * 时间：小于
     */
    Time_LT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return time(dslQueryField, TimePath::lt);
        }
    },
    /**
     * 时间：小于等于
     */
    Time_LE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return time(dslQueryField, TimePath::loe);
        }
    },
    /**
     * 时间：大于
     */
    Time_GT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return time(dslQueryField, TimePath::gt);
        }
    },
    /**
     * 时间：大于等于
     */
    Time_GE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return time(dslQueryField, TimePath::goe);
        }
    },
    /**
     * 日期时间：等于
     */
    DateTime_EQ {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return dateTime(dslQueryField, DateTimePath::eq);
        }
    },
    /**
     * 日期时间：小于
     */
    DateTime_LT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return dateTime(dslQueryField, DateTimePath::lt);
        }
    },
    /**
     * 日期时间：小于等于
     */
    DateTime_LE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return dateTime(dslQueryField, DateTimePath::loe);
        }
    },
    /**
     * 日期时间：大于
     */
    DateTime_GT {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return dateTime(dslQueryField, DateTimePath::gt);
        }
    },
    /**
     * 日期时间：大于等于
     */
    DateTime_GE {
        @Override
        public Optional<BooleanExpression> doAccept(DslQueryField dslQueryField) {
            return dateTime(dslQueryField, DateTimePath::goe);
        }
    };

    /**
     * 通过查询字段获取条件表达式
     *
     * @param dslQueryField 查询字段
     * @return 条件表达式
     */
    protected abstract Optional<BooleanExpression> doAccept(DslQueryField dslQueryField);

    /**
     * 通过查询字段获取条件表达式
     *
     * @param dslQueryField 查询字段
     * @return 条件表达式
     */
    public BooleanExpression accept(DslQueryField dslQueryField) {
        return doAccept(dslQueryField).orElse(null);
    }

    /**
     * 字符串条件表达式
     *
     * @param dslQueryField 查询字段
     * @param function      匹配方式
     * @return 条件表达式
     */
    public Optional<BooleanExpression> string(DslQueryField dslQueryField, BiFunction<StringPath, String, BooleanExpression> function) {
        List<Path> paths = dslQueryField.getPaths();
        String val = StrUtil.toString(dslQueryField.getValue());
        return paths.stream().map(p -> function.apply((StringPath) p, val)).reduce(BooleanExpression::or);
    }

    /**
     * 字符串条件表达式（IN）
     *
     * @param dslQueryField 查询字段
     * @param function      匹配方式
     * @return 条件表达式
     */
    public Optional<BooleanExpression> stringIn(DslQueryField dslQueryField, BiFunction<StringPath, List<String>, BooleanExpression> function) {
        List<String> valueList = dslQueryField.getListValue().stream()
                .filter(Objects::nonNull)
                .map(StrUtil::toString)
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(valueList)) {
            return Optional.empty();
        }
        return dslQueryField.getPaths().stream()
                .map(p -> function.apply((StringPath) p, valueList))
                .reduce(BooleanExpression::or);
    }

    /**
     * 字符串条件表达式（批量）
     *
     * @param dslQueryField 查询字段
     * @param function      匹配方式
     * @return 条件表达式
     */
    public Optional<BooleanExpression> stringBatch(DslQueryField dslQueryField, BiFunction<StringPath, String, BooleanExpression> function) {
        List<Path> paths = dslQueryField.getPaths();
        Object value = dslQueryField.getValue();
        List<Object> list = new ArrayList<>();
        if (value instanceof String) {
            list.addAll(Arrays.asList(((String) value).split("[ ,，;；]")));
        } else if (value instanceof Iterable) {
            ((Iterable) value).forEach(list::add);
        } else if (ArrayUtil.isArray(value)) {
            list.addAll(Arrays.asList((Object[]) value));
        }
        List<String> valueList = list.stream()
                .filter(Objects::nonNull)
                .map(o -> StrUtil.trim(StrUtil.toString(o)))
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(valueList)) {
            return Optional.empty();
        }
        return paths.stream()
                .flatMap(p -> valueList.stream().map(val -> function.apply((StringPath) p, val)))
                .reduce(BooleanExpression::or);
    }

    /**
     * 数字条件表达式
     *
     * @param dslQueryField 查询字段
     * @param function      匹配方式
     * @return 条件表达式
     */
    public <N extends Number & Comparable<?>> Optional<BooleanExpression> number(DslQueryField dslQueryField, BiFunction<NumberPath<N>, N, BooleanExpression> function) {
        List<Path> paths = dslQueryField.getPaths();
        N val = (N) dslQueryField.getValue();
        return paths.stream().map(p -> function.apply((NumberPath<N>) p, val)).reduce(BooleanExpression::or);
    }

    /**
     * 数字条件表达式（IN）
     *
     * @param dslQueryField 查询字段
     * @param function      匹配方式
     * @return 条件表达式
     */
    public <N extends Number & Comparable<?>> Optional<BooleanExpression> numberIn(DslQueryField dslQueryField, BiFunction<NumberPath<N>, List<N>, BooleanExpression> function) {
        List<N> valueList = dslQueryField.getListValue().stream().filter(Objects::nonNull).map(v -> (N) v).collect(Collectors.toList());
        if (CollUtil.isEmpty(valueList)) {
            return Optional.empty();
        }
        return dslQueryField.getPaths().stream()
                .map(p -> function.apply((NumberPath<N>) p, valueList))
                .reduce(BooleanExpression::or);
    }

    /**
     * 日期条件表达式
     *
     * @param dslQueryField 查询字段
     * @param function      匹配方式
     * @return 条件表达式
     */
    public <D extends Comparable> Optional<BooleanExpression> date(DslQueryField dslQueryField, BiFunction<DatePath<D>, D, BooleanExpression> function) {
        List<Path> paths = dslQueryField.getPaths();
        D val = (D) dslQueryField.getValue();
        return paths.stream().map(p -> function.apply((DatePath<D>) p, val)).reduce(BooleanExpression::or);
    }

    /**
     * 时间条件表达式
     *
     * @param dslQueryField 查询字段
     * @param function      匹配方式
     * @return 条件表达式
     */
    public <T extends Comparable> Optional<BooleanExpression> time(DslQueryField dslQueryField, BiFunction<TimePath<T>, T, BooleanExpression> function) {
        List<Path> paths = dslQueryField.getPaths();
        T val = (T) dslQueryField.getValue();
        return paths.stream().map(p -> function.apply((TimePath<T>) p, val)).reduce(BooleanExpression::or);
    }

    /**
     * 时间条件表达式
     *
     * @param dslQueryField 查询字段
     * @param function      匹配方式
     * @return 条件表达式
     */
    public <DT extends Comparable> Optional<BooleanExpression> dateTime(DslQueryField dslQueryField, BiFunction<DateTimePath<DT>, DT, BooleanExpression> function) {
        List<Path> paths = dslQueryField.getPaths();
        DT val = (DT) dslQueryField.getValue();
        return paths.stream().map(p -> function.apply((DateTimePath<DT>) p, val)).reduce(BooleanExpression::or);
    }
}
